package com.zlyx.easy.mock.reader;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.zlyx.easy.core.reflect.ClassBody;
import com.zlyx.easy.core.reflect.ClassBuilder;
import com.zlyx.easy.core.utils.FileUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.core.utils.StringUtils;

/**
 * 类读取工具
 *
 * @Author 赵光
 * @Desc ClassReader
 * @Date 2020年5月30日
 */
public class MockReader {

	public static String METHODBODY = "\n    @Test\n    public void ${NAME}() throws Exception {\n${BODY}\n    }";

	/**
	 * mock类
	 *
	 * @param cls 类
	 * @return
	 * @throws Exception
	 */
	public static ClassBody mock(Class<?> cls) throws Exception {
		return mock(FileUtils.getJavaFile(cls));
	}

	/**
	 * mock类
	 *
	 * @param cls  类
	 * @param name 方法名
	 * @return
	 * @throws Exception
	 */
	public static ClassBody mock(File file) throws Exception {
		FileInputStream in = new FileInputStream(file);
		CompilationUnit cu = JavaParser.parse(in);
		if (cu.getTypes().size() > 0) {
			TypeDeclaration<?> typeDeclaration = cu.getTypes().get(0);
			String packagePath = cu.getPackageDeclaration().get().getNameAsString();
			String className = typeDeclaration.getNameAsString();
			if (cu.getInterfaceByName(className).isPresent()) {
				return null;
			}
			ClassBody classBody = ClassBuilder.newBody(packagePath, className + "Test");
			// 处理导入类
			for (ImportDeclaration importDeclaration : cu.getImports()) {
				classBody.importClass(importDeclaration.toString());
			}
			classBody.importClass("import org.junit.*;\n");
			classBody.importClass("import org.junit.runner.*;\n");
			classBody.importClass("import org.mockito.*;\n");
			classBody.importClass("import org.powermock.api.mockito.*;\n");
			classBody.importClass("import org.powermock.modules.junit4.*;\n");
			// 处理注释+注解
			String header = "";
			if (typeDeclaration.hasJavaDocComment()) {
				header = typeDeclaration.getJavadocComment().get().toString();
			}
			classBody.header(header + "@RunWith(PowerMockRunner.class)\n");

			// 处理需要注入的Bean对象
			Map<String, String> props = new HashMap<>();
			props.put("@Autowired", "\n    @Mock");
			props.put("private", "    private");
			props.put("protected", "    protected");
			props.put("public", "    public");
			for (FieldDeclaration fieldDeclaration : typeDeclaration.getFields()) {
				if (fieldDeclaration.getAnnotationByClass(Autowired.class).isPresent()
						|| fieldDeclaration.getAnnotationByClass(Autowired.class).isPresent()) {
					classBody.field(fieldDeclaration.getElementType().asString(),
							StringUtils.replaceAll(fieldDeclaration.toString(), props));
				}
			}
			String mockBeanString = "\n    @InjectMocks\n    private " + className + " "
					+ StringUtils.toLowCaseFirst(className) + ";";
			classBody.field(StringUtils.toLowCaseFirst(className), mockBeanString);
			new VoidVisitorAdapter<Void>() {
				@Override
				public void visit(MethodDeclaration methodDeclaration, Void arg) {
					String name = methodDeclaration.getName().asString();
					if (methodDeclaration.isPublic()) {
						String nameString = "test" + StringUtils.toUpCaseFirst(name);
						NodeList<Parameter> parameters = methodDeclaration.getParameters();
						String parameterString = "";
						String bodyString = "";
						if (parameters.isNonEmpty()) {
							for (Parameter parameter : parameters) {
								parameterString += ", " + parameter.getName().asString();
								bodyString += "        " + parameter.getType().asString() + " "
										+ parameter.getName().asString();
								String typeName = parameter.getType().asString();
								if (ObjectUtils.in(typeName, "int", "long", "double")) {
									bodyString += " = 0;\n";
								} else if (ObjectUtils.in(typeName, "boolean")) {
									bodyString += " = false;\n";
								} else {
									bodyString += " = null;\n";
								}
							}
							parameterString = parameterString.substring(2);
						}
						bodyString += "        ";
						if (methodDeclaration.isStatic()) {
							bodyString += className;
						} else {
							bodyString += StringUtils.toLowCaseFirst(className);
						}
						bodyString += "." + name + "(" + parameterString + ");";
						Map<String, String> props = new HashMap<>();
						props.put("${NAME}", nameString);
						props.put("${PARAMTER}", parameterString);
						props.put("${BODY}", bodyString);
						classBody.method(nameString, StringUtils.replaceAll(METHODBODY, props));
					}
				}
			}.visit(cu, null);
			return classBody;
		}
		return null;
	}

}
